home *** CD-ROM | disk | FTP | other *** search
/ Archive Magazine CD 1995 / Archive Magazine CD 1995.iso / discs / prog_disc / volume_5 / issue_08 / boota / C-source / c / 1stMake next >
Encoding:
Text File  |  1991-12-16  |  52.5 KB  |  1,450 lines

  1. /* > c.1stMake  - (c) Paul Witheridge - Version 2.00 - 06 Dec 1991   */
  2.  
  3. /*===================================================================*/
  4. /*                                                                   */
  5. /*  1stMake - converts text files to 1stWord+ files                  */
  6. /*  -------                                                          */
  7. /*                                                                   */
  8. /*  This utility converts plain text files to 1stWord+ files in an   */
  9. /*  intelligent manner, so that (hopefully) the resulting file can   */
  10. /*  be re-formatted using 1stWord+ without losing indents, hanging   */
  11. /*  indents, etc.                                                    */
  12. /*                                                                   */
  13. /*-------------------------------------------------------------------*/
  14. /*                                                                   */
  15. /*  COPYRIGHT NOTICE                                                 */
  16. /*                                                                   */
  17. /*  1stMake is subject to Copyright.                                 */
  18. /*                                                                   */
  19. /*  Permission is granted by the author to any recipient of this     */
  20. /*  material to use and make/disseminate copies of the application   */
  21. /*  provided that no charges are made for doing so (other than to    */
  22. /*  cover any cost of media or postage) and that this notice is      */
  23. /*  included with all copies.                                        */
  24. /*                                                                   */
  25. /*===================================================================*/
  26.  
  27. #include "kernel.h"                     /* ARC specifics             */
  28. #include <ctype.h>                      /* Character handling        */
  29. #include <limits.h>                     /* Implementation limits     */
  30. #include <stddef.h>                     /* Standard definitions      */
  31. #include <stdio.h>                      /* Input/output              */
  32. #include <stdlib.h>                     /* General utilities         */
  33. #include <string.h>                     /* String handling           */
  34. #include "GetDirs.h"                    /* GetDirs header            */
  35. #include "ArgFuncs.h"                   /* Arg functions etc         */
  36. #include "Useful.h"                     /* Useful definitions        */
  37.  
  38. /*-------------------------------------------------------------------*/
  39. /*  Global data declarations and definitions.                        */
  40. /*-------------------------------------------------------------------*/
  41.  
  42. static char flags = '\0' ;              /* Processing flags          */
  43.   #define flgfnd 0x01                   /* Found file to process     */
  44.   #define flglfo 0x02                   /* List file is open         */
  45.   #define flglst 0x10                   /* List mode                 */
  46.   #define flgovr 0x20                   /* Overwrite mode            */
  47.   #define flgrcs 0x40                   /* Recursive mode            */
  48.   #define flgtst 0x80                   /* Test mode                 */
  49.  
  50. static int filecount = 0 ;              /* Count of files processed. */
  51.  
  52. static const char *argptr = NULL ;      /* Pointer to file spec.     */
  53. static const char *pfxptr = "_"  ;      /* Pointer to output prefix  */
  54. static const char *lstptr = NULL ;      /* Pointer to list file spec */
  55. static FILE *listfile = NULL ;          /* Pointer to list file      */
  56.  
  57. /*-------------------------------------------------------------------*/
  58. /*  Define settings for line flags.                                  */
  59. /*-------------------------------------------------------------------*/
  60.  
  61. #define brkflg 0x01                     /* Break at end of this line */
  62. #define hinflg 0x02                     /* Hanging indent            */
  63. #define itmflg 0x04                     /* List item                 */
  64. #define pinflg 0x08                     /* Paragraph indent flag     */
  65. #define hrdflg 0x10                     /* Hard spaces flag          */
  66.  
  67. /*-------------------------------------------------------------------*/
  68. /*  Define 1stWord+ standard file header.                            */
  69. /*-------------------------------------------------------------------*/
  70.  
  71. static const char docheader[] =
  72. {
  73.   0x1F, 0x30,                     /* File Start                      */
  74.   0x36, 0x36,                     /* Form length                     */
  75.   0x30, 0x31,                     /* Top of form margin              */
  76.   0x30, 0x33,                     /* Header Margin                   */
  77.   0x30, 0x33,                     /* Footer Margin                   */
  78.   0x30, 0x35,                     /* Bottom of form margin           */
  79.   0x38, 0x30, 0x30,
  80.   0x0A,                           /* End of first line               */
  81.  
  82.   0x1F, 0x31,                     /* Left hdg text would go here     */
  83.   0x1F,                           /* Centre hdg text would go here   */
  84.   0x1F,                           /* Right hdg text would go here    */
  85.   0x0A,                           /* End of hdg line                 */
  86.  
  87.   0x1F, 0x32,                     /* Left ftg text would go here     */
  88.   0x1F,                           /* Centre ftg text would go here   */
  89.   0x1F,                           /* Right ftg text would go here    */
  90.   0x0A,                           /* End of ftg line                 */
  91.  
  92.   0x1F, 0x46, 0x30,               /* Start of footnote line          */
  93.   0x31,                           /* Lines before footnote           */
  94.   0x31,                           /* Lines after footnote            */
  95.   0x30, 0x30,
  96.   0x33, 0x30,                     /* Length of separator line        */
  97.   0x0A,                           /* End of footnote line            */
  98.  
  99.   0x1F, 0x39,                     /* Start of default ruler          */
  100.   0x5B,                           /* Start of TABS definition        */
  101.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  1st TAB                        */
  102.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  2nd TAB                        */
  103.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  3rd TAB                        */
  104.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  4th TAB                        */
  105.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  5th TAB                        */
  106.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  6th TAB                        */
  107.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  7th TAB                        */
  108.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  8th TAB                        */
  109.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  9th TAB                        */
  110.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /* 10th TAB                        */
  111.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /* 11th TAB                        */
  112.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /* 12th TAB                        */
  113.   0x2E, 0x2E, 0x2E,
  114.   0x5D,                           /* End of tabs                     */
  115.   0x30,                           /* Pitch Pica/Elite/Cndnsd/Expnded */
  116.   0x30,                           /* Justification off/on            */
  117.   0x31,                           /* Line spacing                    */
  118.   0x0A,                           /* End of ruler line               */
  119.   0x1B, 0x80
  120. } ;
  121.  
  122. /*-------------------------------------------------------------------*/
  123. /*  Declare functions local in scope to this file.                   */
  124. /*-------------------------------------------------------------------*/
  125.  
  126. static enum boolean cnvtfunc(           /* Convert function          */
  127.   const char *path,
  128.   direntry *ptr) ;
  129.  
  130. static int leadblanks(                  /* Count leading blanks      */
  131.   const char *lineptr) ;
  132.  
  133. static enum boolean isitem(             /* Tests for list item       */
  134.   const char *lineptr,
  135.   const int indent) ;
  136.  
  137. static enum boolean isnumbered(         /* Test for 1-2 digit number */
  138.   const char *p,
  139.   const int l) ;
  140.  
  141. static enum boolean isroman(            /* Test for roman numeral    */
  142.   const char *p,
  143.   const int l) ;
  144.  
  145. static enum boolean isterm(             /* Tests for definition term */
  146.   const char *lineptr,
  147.   int len,
  148.   int indent,
  149.   int nextindent) ;
  150.  
  151. static enum boolean istag(              /* Test for certain GML tags */
  152.   const char *lineptr) ;
  153.  
  154. static enum boolean isshort(            /* Test for short line       */
  155.   const char *lineptr,
  156.   const int len,
  157.   const int brklen) ;
  158.  
  159. static void sethangingindent(           /* Set hanging indent        */
  160.   int line,
  161.   int nlines,
  162.   int nextindent,
  163.   char *(*lineptrs)[],
  164.   char (*lineflgs)[]) ;
  165.  
  166. static void putdup(                     /* Repeat character to file  */
  167.   const int c,
  168.   const int n,
  169.   FILE *fileptr) ;
  170.  
  171. /*===================================================================*/
  172. /*                                                                   */
  173. /*  Main program                                                     */
  174. /*  ------------                                                     */
  175. /*                                                                   */
  176. /*  Analyse arguments. Then call the "GetDirentrys" function to      */
  177. /*  read all the file-names in the specified directory. Provide      */
  178. /*  pointer to function to process the specified files.              */
  179. /*                                                                   */
  180. /*===================================================================*/
  181.  
  182. int main(
  183.   const int argc,                       /* Number of arguments       */
  184.   const char *const argv[])             /* Array of pointers to args */
  185. {
  186.   /*-----------------------------------------------------------------*/
  187.   /*  Local definitions.                                             */
  188.   /*-----------------------------------------------------------------*/
  189.  
  190.   int returncode ;
  191.  
  192.   static const char **posval[] = /* Ptrs to ptrs to posit'nal values */ 
  193.   {
  194.     &argptr
  195.   } ;
  196.  
  197.   static const char options[] =  /* Used to match option argument    */
  198.   {
  199.     'L',                            /* List mode                     */
  200.     'O',                            /* Overwrite mode                */
  201.     'P',                            /* Output prefix                 */
  202.     'R',                            /* Recursive mode                */
  203.     'T',                            /* Test mode                     */
  204.     '0'                             /* Null byte terminator          */
  205.   } ;
  206.  
  207.   static const char optflags[] = /* Used to set option flags         */
  208.   {
  209.     flglst,                         /* List mode                     */
  210.     flgovr,                         /* Overwrite mode                */
  211.     0,                              /* Output prefix                 */
  212.     flgrcs,                         /* Recursive mode                */
  213.     flgtst,                         /* Test mode                     */
  214.   } ;
  215.  
  216.   static const char **optval[] = /* Ptrs to ptrs to option values    */
  217.   {
  218.     &lstptr,                        /* Optional list file name       */
  219.     NULL,                           /* No value allowed              */
  220.     &pfxptr,                        /* Output file prefix            */
  221.     NULL,                           /* No value allowed              */
  222.     NULL,                           /* No value allowed              */
  223.   } ;
  224.  
  225.   static const char helpdata[] = /* Help text to be displayed        */
  226.  
  227.     "1stMake converts plain ASCII text files into 1stWord+ "
  228.     "documents. It attempts to insert the correct 1stWord+ "
  229.     "format control characters for hanging indents and lists "
  230.     "so that they can be reflowed correctly when reformatting "
  231.     " with 1stWord+.\x1f"
  232.     "\x1f"
  233.     "WARNING:\x01" "This utility converts files IN-PLACE if the "
  234.     "'-o' option is used to replace the input text file with "
  235.     "the output 1stWord+ document.\x1f"
  236.     "\x1f"
  237.     "Syntax:\x01" "*1stMake  [path.]object  [options]\x1f"
  238.     "\x1f"
  239.     "object:\x01" "Specifies one of the following:\x1f"
  240.     "\x01" "(a) a single non-wildcarded file name\x1f"
  241.     "\x01" "(b) a single non-wildcarded directory name\x1f"
  242.     "\x01" "(c) a wildcarded name.\x1f"
  243.     "\x1f"
  244.     "\x01" "In case (a) the file is converted provided that "
  245.     "it has a file-type of TEXT.\x1f"
  246.     "\x1f"
  247.     "\x01" "In case (b) all TEXT files in the specified directory "
  248.     "are converted. If the RECURSION option is specified all TEXT "
  249.     "files in all subdirectories are also converted.\x1f"
  250.     "\x1f"
  251.     "\x01" "In case (c) all matching TEXT files are converted. "
  252.     "If no matching files are found, then the first matching "
  253.     "directory name is taken and all TEXT files therein are "
  254.     "converted. If the RECURSION option is specified, all "
  255.     "matching TEXT files are converted, plus all TEXT files "
  256.     "in all subdirectories.\x1f"
  257.     "\x1f"
  258.     "\x01" "In all cases, only files which have a filetype of "
  259.     "TEXT (hex FFF) will be converted.\x1f"
  260.     "\x1f"
  261.     "path:\x01" "Specifies the directory to be searched for the "
  262.     "object file/directory. If omitted the current directory "
  263.     "is searched.\x1f"
  264.     "\x1f"
  265.     "options:\x01" "Specifies processing options which can be "
  266.     "one or more of the following:\x1f"
  267.     "\x1f"
  268.     "\x01" "-l xxx\x02" "-\x03" "LIST mode; the input file "
  269.     "contents are listed to file 'xxx', each line prefixed by "
  270.     "flags showing the conversion action for that line; if 'xxx' "
  271.     "is omitted the list is written to the standard output "
  272.     "stream.\x1f"
  273.     "\x1f"
  274.     "\x01" "-o\x02" "-\x03" "OVERWRITE mode; output file will "
  275.     "have same name and will replace input file.\x1f"
  276.     "\x1f"
  277.     "\x01" "-p xxx\x02" "-\x03" "specify PREFIX which will be "
  278.     "used to create name for output file if '-o' is not specified; "
  279.     "'xxx' can be one or more characters including directory "
  280.     "specification (e.g. 'txtdir.'); if not specified a default "
  281.     "of '_' is used.\x1f"
  282.     "\x1f"
  283.     "\x01" "-r\x02" "-\x03" "RECURSION mode which causes all eligible "
  284.     "files in all subdirectories to be converted.\x1f"
  285.     "\x1f"
  286.     "\x01" "-t\x02" "-\x03" "TEST mode; a list of files to be "
  287.     "converted is displayed but no output is actually written; "
  288.     "useful for checking when specifying a directory or wildcarded "
  289.     "filename.\x1f"
  290.     "\x1f"
  291.     "If option '-l' (list mode) is specified, the input file contents "
  292.     "are listed with each line prefixed by flag characters indicating "
  293.     "how 1stMake will convert that line. The flag possible flag "
  294.     "characters are as follows:\x1f"
  295.     "\x1f"
  296.     "\x04" "B\x05" "-\x06" "line assumed to be 'break' in formatting "
  297.     "(end of paragraph) and no space will be inserted before new "
  298.     "line character at end of line.\x1f"
  299.     "\x1f"
  300.     "\x04" "P\x05" "-\x06" "line assumed to be start of paragraph "
  301.     "and will be indented with fixed spaces (as produced by TAB or "
  302.     "FIXED SPACE key).\x1f"
  303.     "\x1f"
  304.     "\x04" "L\x05" "-\x06" "line assumed to be the start of an item "
  305.     "in a list and the initial item identification (e.g. paragraph "
  306.     "number) will be followed by an 'indent' space plus 'stretch' "
  307.     "spaces (as produced by INDENT key)\x1f"
  308.     "\x1f"
  309.     "\x04" "I\x05" "-\x06" "line will be indented with an 'indent' "
  310.     "space plus 'stretch' spaces (as produced by INDENT key)\x1f"
  311.     "\x1f"
  312.     "\x04" "A\x05" "-\x06" "line is assumed to be non-formattable "
  313.     "('as is') and all spaces will be converted to fixed spaces.\x1f"
  314.     "\x1f"
  315.     "1stMake - copyright Paul Witheridge, 1991\x1f"
  316.     "\x1f" ;
  317.  
  318.   static const unsigned char helptabs[] =  /* Help text tab settings */
  319.   {
  320.     1,10,17,19,2,4,6
  321.   } ;
  322.  
  323.   /*-----------------------------------------------------------------*/
  324.   /*  Executable statements                                          */
  325.   /*                                                                 */
  326.   /*  First display sign-on message with version/date ids.           */
  327.   /*-----------------------------------------------------------------*/
  328.  
  329.   puts("\n1stMake Version 2.00 - 06 December 1991\n") ;
  330.   
  331.   /*-----------------------------------------------------------------*/
  332.   /*  Analyse arguments for file-name and option flags.              */
  333.   /*-----------------------------------------------------------------*/
  334.  
  335.   analargs(argc,argv,1,posval,options,&flags,optflags,optval) ;
  336.  
  337.   /*-----------------------------------------------------------------*/
  338.   /*  Set paged scrolling mode                                       */
  339.   /*-----------------------------------------------------------------*/
  340.  
  341.   _kernel_oswrch(12) ;
  342.   _kernel_oswrch(14) ;
  343.  
  344.   puts(" PRESS SHIFT KEY TO ALLOW WINDOW TO SCROLL\n") ;
  345.   
  346.   /*-----------------------------------------------------------------*/
  347.   /*  If no file name operand just display help text.                */
  348.   /*-----------------------------------------------------------------*/
  349.  
  350.   if ( argptr == NULL )
  351.   {
  352.     displaytext(helpdata,helptabs) ;
  353.     _kernel_oswrch(15) ;
  354.     return 0 ;
  355.   }
  356.  
  357.   /*-----------------------------------------------------------------*/
  358.   /*  If TEST MODE issue message.                                    */
  359.   /*-----------------------------------------------------------------*/
  360.  
  361.   if ( flags & flgtst )
  362.   {
  363.     puts("TEST MODE (files will be identified, not converted).\n") ;
  364.   }
  365.  
  366.   /*-----------------------------------------------------------------*/
  367.   /*  Invoke GetDirs function to read the directory entry(s) of the  */
  368.   /*  file(s) to be processed. Pass it a pointer of a processing     */
  369.   /*  function to be called.                                         */
  370.   /*-----------------------------------------------------------------*/
  371.  
  372.   returncode = 4 ;
  373.  
  374.   if ( getdirentrys(argptr,
  375.        ( flags & flgrcs ? RECURSE_ALWAYS : RECURSE_ONCE ),cnvtfunc) )
  376.   {
  377.     if ( flags & flgfnd )
  378.     {
  379.       printf("\n%d text file(s) ",filecount) ;
  380.       if ( flags & flgtst )
  381.       {
  382.         printf("would be ") ;
  383.       }
  384.       puts("converted.\n") ;
  385.       returncode = 0 ;
  386.     }
  387.     else
  388.     {
  389.       printf("No text files found matching '%s'\n",argptr) ;
  390.       beep() ;
  391.     }
  392.   }
  393.   
  394.   /*-----------------------------------------------------------------*/
  395.   /*  Close list file if open.                                       */
  396.   /*-----------------------------------------------------------------*/
  397.  
  398.   if ( flags & flglfo )
  399.   {
  400.     fclose(listfile) ;
  401.   }
  402.   
  403.   /*-----------------------------------------------------------------*/
  404.   /*  Return to caller. All done.                                    */
  405.   /*-----------------------------------------------------------------*/
  406.  
  407.   _kernel_oswrch(15) ;
  408.   return returncode ;
  409. }
  410.  
  411. /*===================================================================*/
  412. /*                                                                   */
  413. /*  cnvtfunc  -  perform actual file conversion                      */
  414. /*  --------                                                         */
  415. /*                                                                   */
  416. /*  This function is called by the "getdirentrys" function for each  */
  417. /*  file-name that it encounters.                                    */
  418. /*                                                                   */
  419. /*  Only files which have a filetype of "text" (0xFFF) will be       */
  420. /*  processed.                                                       */
  421. /*                                                                   */
  422. /*===================================================================*/
  423.  
  424. static enum boolean cnvtfunc(
  425.   const char *path,                     /* Pointer to path name.     */
  426.   direntry *ptr)                        /* Pointer to direntry info. */
  427. {
  428.   /*-----------------------------------------------------------------*/
  429.   /*  Local definitions.                                             */
  430.   /*-----------------------------------------------------------------*/
  431.  
  432.   char *infile ;                      /* Ptr to path + leafname      */
  433.   char *oufile ;                      /* Ptr tp path + leafname      */
  434.   int result ;                        /* Result from OS-File SWI     */
  435.   char *workarea ;                    /* Ptr to start of work area   */
  436.   char *workend ;                     /* Ptr to end of work area     */
  437.   char *iptr ;                        /* Working pointer             */
  438.   char *optr ;                        /* Working pointer             */
  439.   _kernel_osfile_block osfileblk ;    /* OS_File parameter block     */
  440.   char *(*lineptrs)[] ;               /* Ptr to line ptr array       */
  441.   char *lineptr ;                     /* Ptr to current line         */
  442.   char (*lineflgs)[] ;                /* Ptr to line flags array     */
  443.   int lineflg ;                       /* Copy of current line flags  */
  444.   int len ;                           /* Length of current line      */
  445.   int maxlen ;                        /* Maximum length of any line  */
  446.   int brklen ;                        /* Short line threshold        */
  447.   int line ;                          /* Current line number         */
  448.   int nlines ;                        /* Number of lines             */
  449.   int nblanks ;                       /* Working blanks counter      */
  450.   int indent ;                        /* Indent of current line      */
  451.   int nextindent ;                    /* Indent of next line         */
  452.   int minindent ;                     /* Minimum indent of any line  */
  453.   FILE *fileptr ;                     /* Output file pointer         */
  454.   FILE *lstfptr ;                     /* List file pointer           */
  455.   int i,j,k ;                         /* Working integers            */
  456.   int c ;                             /* Working character           */
  457.   char flagprnt[8] ;                  /* Flag display work area      */
  458.  
  459.   static const char toomanylines[] =
  460.     "'%s' contains too many lines to process" ;
  461.  
  462.   /*-----------------------------------------------------------------*/
  463.   /*  Executable statements                                          */
  464.   /*                                                                 */
  465.   /*  Return immediatley if not a text file.                         */
  466.   /*-----------------------------------------------------------------*/
  467.  
  468.   if ( ptr->type != 0xfff )
  469.   {
  470.     return TRUE ;
  471.   }
  472.  
  473.   /*-----------------------------------------------------------------*/
  474.   /*  If test mode without "-l" option, just list file name.         */
  475.   /*-----------------------------------------------------------------*/
  476.  
  477.   if ( (flags & (flgtst | flglst)) == flgtst )
  478.   {
  479.     if ( !(flags & flgfnd) )
  480.     {
  481.       flags |= flgfnd ;
  482.       puts("The following files would be converted "
  483.            "to 1stWord+ documents:\n") ;
  484.     }
  485.     printf("%s%s\n",path,ptr->name) ;
  486.     filecount++ ;
  487.     return TRUE ;
  488.   }
  489.  
  490.   /*-----------------------------------------------------------------*/
  491.   /*  Create input and output filespecs                              */
  492.   /*-----------------------------------------------------------------*/
  493.   
  494.   if ( ( infile = malloc(2 * (strlen(path) + strlen(ptr->name) + 1)
  495.                          + strlen(pfxptr) ) ) == NULL )
  496.   {
  497.     puts("Out of memory") ;
  498.     return FALSE ;
  499.   } 
  500.   
  501.   oufile = infile + sprintf(infile,"%s%s",path,ptr->name) + 1 ;
  502.   sprintf(oufile,"%s%s%s",path,flags & flgovr ? "" : pfxptr,ptr->name) ;
  503.   
  504.   /*-----------------------------------------------------------------*/
  505.   /*  Check input file has non-zero length.                          */
  506.   /*-----------------------------------------------------------------*/
  507.  
  508.   if ( ptr->length == 0 )
  509.   {
  510.     printf("'%s' is empty\n",infile) ;
  511.   }
  512.  
  513.   /*-----------------------------------------------------------------*/
  514.   /*  Allocate memory for file and load it.                          */
  515.   /*-----------------------------------------------------------------*/
  516.  
  517.   if ( (workarea = malloc((ptr->length)+1)) == NULL )
  518.   {
  519.     printf("'%s' too large to load",infile) ;
  520.     goto error4 ;
  521.   }
  522.  
  523.   workend = workarea + ptr->length ;
  524.  
  525.   osfileblk.load  = (int)workarea ;
  526.   osfileblk.exec  = 0 ;
  527.   result = _kernel_osfile(255,infile,&osfileblk) ;
  528.  
  529.   if ( result == _kernel_ERROR )
  530.   {
  531.     printf("'%s' load failed - %s\n",infile,
  532.           _kernel_last_oserror()->errmess) ;
  533.     goto error3 ;
  534.   }
  535.  
  536.   /*-----------------------------------------------------------------*/
  537.   /*  If missing new-line at end of file, add one (provided that     */
  538.   /*  file is non-zero length).                                      */
  539.   /*-----------------------------------------------------------------*/
  540.  
  541.   if ( workend > workarea && *(workend-1) != '\n' )
  542.   {
  543.     *(workend++) = '\n' ;
  544.     printf("No newline character at end of '%s' - inserted\n",infile) ;
  545.     beep() ;
  546.   }
  547.  
  548.   /*-----------------------------------------------------------------*/
  549.   /*  Count number of newline characters in file. Substitute null    */
  550.   /*  bytes for the newline characters. Also determine the minimum   */
  551.   /*  indentation of any line.                                       */
  552.   /*-----------------------------------------------------------------*/
  553.  
  554.   indent = minindent = INT_MAX ;
  555.   nblanks = nlines = 0 ;
  556.  
  557.   for ( iptr = workarea ;iptr < workend ; iptr++ )
  558.   {
  559.     switch (*iptr)
  560.     {
  561.       /*-------------------------------------------------------------*/
  562.       /*  Process newline character.                                 */
  563.       /*-------------------------------------------------------------*/
  564.  
  565.       case '\n' :
  566.         
  567.         *iptr = '\0' ;
  568.         nlines++ ;
  569.         if ( indent < minindent )
  570.         {
  571.           minindent = indent ;
  572.         }
  573.         nblanks = 0 ;
  574.         indent  = INT_MAX ;
  575.         break ;
  576.  
  577.       /*-------------------------------------------------------------*/
  578.       /*  Process blank.                                             */
  579.       /*-------------------------------------------------------------*/
  580.  
  581.       case ' ' :
  582.         
  583.         nblanks++ ;
  584.         break ;
  585.  
  586.       /*-------------------------------------------------------------*/
  587.       /*  Process other characters                                   */
  588.       /*-------------------------------------------------------------*/
  589.  
  590.       default :
  591.         if ( indent == INT_MAX )
  592.         {
  593.           indent = nblanks ;
  594.         }
  595.     }
  596.   }
  597.  
  598.   /*-----------------------------------------------------------------*/
  599.   /*  Allocate storage for pointer array. Perform second pass        */
  600.   /*  through loaded file setting up pointers, shifting each line    */
  601.   /*  left by minimum indentation amount, truncating any trailing    */
  602.   /*  blanks and computing maximum line length.                      */
  603.   /*-----------------------------------------------------------------*/
  604.  
  605.   if ( (lineptrs = malloc((nlines+2) * sizeof(char *))) == NULL )
  606.   {
  607.     printf(toomanylines,infile) ;
  608.     goto error3 ;
  609.   }
  610.  
  611.   iptr = optr = workarea ;
  612.   maxlen = 0 ;
  613.   (*lineptrs)[0] = (*lineptrs)[nlines+1] = "" ;
  614.  
  615.   for ( line = 1 ; line <= nlines ; line++ )
  616.   {
  617.     (*lineptrs)[line] = lineptr = optr ;
  618.     len = strlen(iptr) ;
  619.     if ( len < minindent )
  620.     {
  621.       iptr += len + 1 ;
  622.     }
  623.     else
  624.     {
  625.       iptr += minindent ;
  626.       for ( ; (*optr = *(iptr++)) != '\0' ; optr++ ) ;
  627.       while ( optr > lineptr )
  628.       {
  629.         if ( *(--optr) != ' ' )
  630.         {
  631.           optr++ ;
  632.           break ;
  633.         }
  634.       }
  635.       if ( (len = optr - lineptr) > maxlen )
  636.       {
  637.         maxlen = len ;
  638.       }
  639.     }
  640.     *(optr++) = '\0' ;
  641.  
  642.   }
  643.  
  644.   brklen = 2 * maxlen / 3 ;
  645.  
  646.   /*-----------------------------------------------------------------*/
  647.   /*  Allocate storage for line flag array                           */
  648.   /*-----------------------------------------------------------------*/
  649.  
  650.   if ( (lineflgs = malloc(nlines+2)) == NULL )
  651.   {
  652.     printf(toomanylines,infile) ;
  653.     goto error2 ;
  654.   }
  655.   memset(lineflgs,0,nlines+2) ;
  656.  
  657.   /*-----------------------------------------------------------------*/
  658.   /*  Perform pass through file setting flags to indicate how the    */
  659.   /*  line should be converted.                                      */
  660.   /*-----------------------------------------------------------------*/
  661.  
  662.   nextindent = leadblanks((*lineptrs)[1]) ;
  663.  
  664.   for ( line = 1 ; line <= nlines ; line++ )
  665.   {
  666.     lineptr = (*lineptrs)[line] ;
  667.     lineflg = (*lineflgs)[line] ;
  668.     len     = strlen(lineptr) ;
  669.     indent  = nextindent ;
  670.     nextindent = leadblanks((*lineptrs)[line+1]) ;
  671.  
  672.     /*---------------------------------------------------------------*/
  673.     /*  If null line encountered or if this line starts with a       */
  674.     /*  period (i.e. is SCRIPT/VS control word) set break flag on    */
  675.     /*  this and on previous line.                                   */
  676.     /*---------------------------------------------------------------*/
  677.  
  678.     if ( *lineptr == '\0' || *lineptr == '.' )
  679.     {
  680.       (*lineflgs)[line-1] |= brkflg ;
  681.       lineflg |= brkflg ;
  682.     }
  683.  
  684.     /*---------------------------------------------------------------*/
  685.     /*  If not a null line then it must be analysed to determine     */
  686.     /*  any special formatting requirements.                         */
  687.     /*---------------------------------------------------------------*/
  688.  
  689.     else
  690.     {
  691.       /*-------------------------------------------------------------*/
  692.       /*  If line begins with a colon (i.e. is a SCRIPT/VS GML       */
  693.       /*  tag) then set break flag on previous line (provided        */
  694.       /*  that it is not one of the GML tags that I prefer NOT       */
  695.       /*  to be treated as a break).                                 */
  696.       /*-------------------------------------------------------------*/
  697.  
  698.       if ( *lineptr == ':' && !istag(lineptr) )
  699.       {
  700.         (*lineflgs)[line-1] |= brkflg ;
  701.       }
  702.  
  703.       /*-------------------------------------------------------------*/
  704.       /*  Count number of punctuation characters in line. If         */
  705.       /*  they make up more than two thirds of the non-blank         */
  706.       /*  characters on the line, treat it as an 'asis' line.        */
  707.       /*-------------------------------------------------------------*/
  708.  
  709.       for ( i = j = k = 0 ; i < len ; i++ )
  710.       {
  711.         c = lineptr[i] ;
  712.         if ( ispunct(c) )
  713.         {
  714.           j++ ;
  715.         }
  716.         else if ( c == ' ' )
  717.         {
  718.           k++ ;
  719.         }
  720.       }
  721.  
  722.       if ( j > 2 * (len - k) / 3 )
  723.       {
  724.         (*lineflgs)[line-1] |= brkflg ;
  725.         lineflg |= brkflg | hrdflg ;
  726.       }
  727.  
  728.       /*-------------------------------------------------------------*/
  729.       /*  Current line indented more than next line.                 */
  730.       /*                                                             */
  731.       /*  If current line is part of hanging indent, assume end      */
  732.       /*  of list item, so set break flag on current line.           */
  733.       /*  If line starts with bullet-like format assume a one        */
  734.       /*  line list item and set break flag on current line and      */
  735.       /*  on previous line. Otherwise assume start of new            */
  736.       /*  paragraph.                                                 */
  737.       /*-------------------------------------------------------------*/
  738.  
  739.       else if ( indent > nextindent )
  740.       {
  741.         if ( lineflg & hinflg )
  742.         {
  743.           lineflg |= brkflg ;
  744.           if ( isitem(lineptr,indent) )
  745.           {
  746.             (*lineflgs)[line-1] |= brkflg ;
  747.             lineflg |= itmflg ;
  748.           }
  749.         }
  750.         else
  751.         {
  752.           (*lineflgs)[line-1] |= brkflg ;
  753.           if( isitem(lineptr,indent) )
  754.           {
  755.             lineflg |= brkflg | itmflg ;
  756.           }
  757.           else
  758.           {
  759.             lineflg |= pinflg ;
  760.             if ( isshort(lineptr,len,brklen) )
  761.             {
  762.               lineflg |= brkflg ;
  763.             }
  764.           }
  765.         }
  766.       }
  767.  
  768.       /*-------------------------------------------------------------*/
  769.       /*  Current line at same indentation as next.                  */
  770.       /*                                                             */
  771.       /*  Set break flag if short line. If line starts with          */
  772.       /*  bullet-like format assume one line list item.              */
  773.       /*-------------------------------------------------------------*/
  774.  
  775.       else if ( indent == nextindent )
  776.       {
  777.         if ( isshort(lineptr,len,brklen) )
  778.         {
  779.           lineflg |= brkflg ;
  780.         }
  781.         if ( isitem(lineptr,indent) )
  782.         {
  783.           (*lineflgs)[line-1] |= brkflg ;
  784.           lineflg |= itmflg | brkflg ;
  785.         }
  786.         if ( !(lineflg & hinflg) )
  787.         {
  788.           if ( indent > 0 )
  789.           {
  790.             (*lineflgs)[line-1] |= brkflg ;
  791.             lineflg |= hinflg ;
  792.             sethangingindent(line,nlines,nextindent,
  793.                   lineptrs,lineflgs) ;
  794.           }
  795.         }
  796.       }
  797.  
  798.       /*-------------------------------------------------------------*/
  799.       /*  Current line is less indented than next line.              */
  800.       /*                                                             */
  801.       /*  Assume start of hanging indent. Set item flag if line      */
  802.       /*  appears to start with bullet-like format or list term.     */
  803.       /*                                                             */
  804.       /*  Set hanging indent flag on all following lines of same     */
  805.       /*  or greater indent than next line.                          */
  806.       /*-------------------------------------------------------------*/
  807.  
  808.       else
  809.       {
  810.         if ( isitem(lineptr,indent) ||
  811.             isterm(lineptr,len,indent,nextindent) )
  812.         {
  813.           (*lineflgs)[line-1] |= brkflg ;
  814.           lineflg |= itmflg ;
  815.         }
  816.         if ( isshort(lineptr,len,brklen) )
  817.         {
  818.           lineflg |= brkflg ;
  819.         }
  820.         sethangingindent(line,nlines,nextindent,lineptrs,lineflgs) ;
  821.       }
  822.     }
  823.     (*lineflgs)[line] = lineflg ;
  824.   }
  825.  
  826.   /*-----------------------------------------------------------------*/
  827.   /*  For debug purposes list document showing flag settings.        */
  828.   /*-----------------------------------------------------------------*/
  829.  
  830.   if ( flags & flglst )
  831.   {
  832.     if ( !(flags & flglfo) )
  833.     {
  834.       if ( lstptr != NULL )
  835.       {
  836.         if ( (listfile = fopen(lstptr,"w")) == NULL )
  837.         {
  838.           printf("Cannot open list file '%s'\n"
  839.                  "Listing will be written to "
  840.                  "standard output stream\n",lstptr) ;
  841.         }
  842.       }
  843.       flags |= flglfo ;
  844.     }
  845.     lstfptr = ( listfile == NULL ) ? stdout : listfile ;
  846.  
  847.     putdup('*',70,lstfptr) ;
  848.     fprintf(lstfptr,"\nConversion listing for file: %s\n",infile) ;
  849.     putdup('*',70,lstfptr) ;
  850.     fputc('\n',lstfptr) ;
  851.  
  852.     for ( line = 1 ; line <= nlines ; line++ )
  853.     {
  854.       lineflg = (*lineflgs)[line] ;
  855.       strcpy(flagprnt,".....  ") ;
  856.       if ( lineflg & brkflg )
  857.       {
  858.         flagprnt[0] = 'B' ;
  859.       }
  860.       if ( lineflg & pinflg )
  861.       {
  862.         flagprnt[1] = 'P' ;
  863.       }
  864.       if ( lineflg & itmflg )
  865.       {
  866.         flagprnt[2] = 'L' ;
  867.       }
  868.       if ( lineflg & hinflg )
  869.       {
  870.         flagprnt[3] = 'I' ;
  871.       }
  872.       if ( lineflg & hrdflg )
  873.       {
  874.         flagprnt[4] = 'A' ;
  875.       }
  876.       fprintf(lstfptr,"%s%s\n",flagprnt,(*lineptrs)[line]) ;
  877.     }
  878.     putdup('*',70,lstfptr) ;
  879.     fputs("\n\n",lstfptr) ;
  880.   }
  881.  
  882.   /*-----------------------------------------------------------------*/
  883.   /*  Unless test mode convert document and replace original file.   */
  884.   /*-----------------------------------------------------------------*/
  885.  
  886.   if ( !(flags & flgtst) )
  887.   {
  888.     /*---------------------------------------------------------------*/
  889.     /*  Open target file for output.                                 */
  890.     /*---------------------------------------------------------------*/
  891.  
  892.     if ( (fileptr = fopen(oufile,"w")) == NULL )
  893.     {
  894.       printf("Cannot open file '%s' - %s\n",oufile,
  895.              _kernel_last_oserror()->errmess) ;
  896.       goto error1 ;
  897.     }
  898.  
  899.     /*---------------------------------------------------------------*/
  900.     /*  Copy 1stWord+ header to output file.                         */
  901.     /*---------------------------------------------------------------*/
  902.  
  903.     for ( i = 0 ; i < sizeof(docheader) ; i++ )
  904.     {
  905.       fputc(docheader[i],fileptr) ;
  906.     }
  907.  
  908.     /*---------------------------------------------------------------*/
  909.     /*  Copy document to output file.                                */
  910.     /*---------------------------------------------------------------*/
  911.  
  912.     nextindent = leadblanks((*lineptrs)[1]) ;
  913.  
  914.     for ( line = 1 ; line <= nlines ; line++ )
  915.     {
  916.       lineptr = (*lineptrs)[line] ;
  917.       lineflg = (*lineflgs)[line] ;
  918.       len     = strlen(lineptr) ;
  919.       indent  = nextindent ;
  920.       nextindent = leadblanks((*lineptrs)[line+1]) ;
  921.  
  922.       i = 0 ;
  923.  
  924.       /*-------------------------------------------------------------*/
  925.       /*  If 'asis' line copy to output file asis.                   */
  926.       /*-------------------------------------------------------------*/
  927.  
  928.       if ( lineflg & hrdflg )
  929.       {
  930.         for ( ; (c = lineptr[i]) != '\0' ; i++ )
  931.         {
  932.           fputc(c,fileptr) ;
  933.         }
  934.       }
  935.  
  936.       /*-------------------------------------------------------------*/
  937.       /*  If 'paragraph' indent, indent with hard spaces.            */
  938.       /*-------------------------------------------------------------*/
  939.  
  940.       else if ( lineflg & pinflg )
  941.       {
  942.         putdup(' ',indent,fileptr) ;
  943.         i = indent ;
  944.       }
  945.  
  946.       /*-------------------------------------------------------------*/
  947.       /*  If hanging indent, indent with indent spaces.              */
  948.       /*-------------------------------------------------------------*/
  949.  
  950.       else if ( lineflg & hinflg )
  951.       {
  952.         fputc(0x1d,fileptr) ;
  953.         putdup(0x1c,indent-1,fileptr) ;
  954.         i = indent ;
  955.       }
  956.  
  957.       /*-------------------------------------------------------------*/
  958.       /*  If start of list item indent with indent spaces.           */
  959.       /*-------------------------------------------------------------*/
  960.  
  961.       if ( lineflg & itmflg )
  962.       {
  963.         j = 0 ;
  964.         c = lineptr[i] ;
  965.         while ( (i < nextindent || j < 2) && c != '\0' )
  966.         {
  967.           if (c == ' ')
  968.           {
  969.             fputc(0x1d,fileptr) ;
  970.             i++ ;
  971.             while ( (i < nextindent || j < 2) &&
  972.                   (c = lineptr[i]) == ' ' )
  973.             {
  974.               fputc(0x1c,fileptr) ;
  975.               i++ ;
  976.             }
  977.           }
  978.           else
  979.           {
  980.             j++ ;
  981.             while ( (c = lineptr[i]) != ' ' && c != '\0' )
  982.             {
  983.               fputc(c,fileptr) ;
  984.               i++ ;
  985.             }
  986.           }
  987.         }
  988.       }
  989.  
  990.       /*-------------------------------------------------------------*/
  991.       /*  Copy rest of line to output, converting blanks to          */
  992.       /*  'soft' blanks.                                             */
  993.       /*-------------------------------------------------------------*/
  994.  
  995.       for ( ; (c = lineptr[i]) != '\0' ; i++ )
  996.       {
  997.         if ( c == ' ' )
  998.         {
  999.           c = 0x1e ;
  1000.         }
  1001.         fputc(c,fileptr) ;
  1002.       }
  1003.  
  1004.       /*-------------------------------------------------------------*/
  1005.       /*  Unless break expected, add a final soft blank to the       */
  1006.       /*  end of the line.                                           */
  1007.       /*-------------------------------------------------------------*/
  1008.  
  1009.       if ( !(lineflg & brkflg) )
  1010.       {
  1011.         fputc(0x1e,fileptr) ;
  1012.       }
  1013.  
  1014.       /*-------------------------------------------------------------*/
  1015.       /*  Finish off with newline character.                         */
  1016.       /*-------------------------------------------------------------*/
  1017.  
  1018.       fputc('\n',fileptr) ;
  1019.     }
  1020.  
  1021.     /*---------------------------------------------------------------*/
  1022.     /*  Close file.                                                  */
  1023.     /*---------------------------------------------------------------*/
  1024.  
  1025.     fclose(fileptr) ;
  1026.   }
  1027.  
  1028.   /*-----------------------------------------------------------------*/
  1029.   /*  Free work area, line pointers, etc.                            */
  1030.   /*-----------------------------------------------------------------*/
  1031.  
  1032.   free(lineflgs) ;
  1033.   free(lineptrs) ;
  1034.   free(workarea) ;
  1035.  
  1036.   /*-----------------------------------------------------------------*/
  1037.   /*  Set record type to that for 1stWord+ Document.                 */
  1038.   /*-----------------------------------------------------------------*/
  1039.  
  1040.   if ( !(flags & flgtst) )
  1041.   {
  1042.     osfileblk.load  = 0xaf8 ;
  1043.     result = _kernel_osfile(18,oufile,&osfileblk) ;
  1044.  
  1045.     if ( result == _kernel_ERROR )
  1046.     {
  1047.       printf("'%s' SETTYPE failed - %s\n",oufile,
  1048.              _kernel_last_oserror()->errmess) ;
  1049.       goto error4 ;
  1050.     }
  1051.  
  1052.     printf("'%s' converted",infile) ;
  1053.     if ( !(flags & flgovr) )
  1054.     {
  1055.       printf(" to '%s'",oufile) ;
  1056.     }
  1057.     putchar('\n') ;
  1058.   }
  1059.  
  1060.   /*-----------------------------------------------------------------*/
  1061.   /*  Return to caller with RC=TRUE.                                 */
  1062.   /*-----------------------------------------------------------------*/
  1063.  
  1064.   free(infile) ;
  1065.   flags |= flgfnd ;
  1066.   filecount++ ;
  1067.   return TRUE ;
  1068.  
  1069.   /*-----------------------------------------------------------------*/
  1070.   /*  Error exits.                                                   */
  1071.   /*-----------------------------------------------------------------*/
  1072.  
  1073.   error1: free(lineflgs) ;
  1074.   error2: free(lineptrs) ;
  1075.   error3: free(workarea) ;
  1076.   error4: free(infile) ;
  1077.           beep() ;
  1078.  
  1079.   return FALSE ;
  1080. }
  1081.  
  1082. /*===================================================================*/
  1083. /*                                                                   */
  1084. /*  leadblanks  -  count number of leading blanks in line            */
  1085. /*  ----------                                                       */
  1086. /*                                                                   */
  1087. /*  This function determines the indent of the line passed as an     */
  1088. /*  argument.                                                        */
  1089. /*                                                                   */
  1090. /*===================================================================*/
  1091.  
  1092. static int leadblanks(
  1093.   const char *lineptr)                  /* Ptr to line text          */
  1094. {
  1095.   int i = 0 ;
  1096.   while ( lineptr[i] == ' ' )
  1097.   {
  1098.     i++ ;
  1099.   }
  1100.   return i ;
  1101. }
  1102.  
  1103. /*===================================================================*/
  1104. /*                                                                   */
  1105. /*  isitem  -  test for list item format                             */
  1106. /*  ------                                                           */
  1107. /*                                                                   */
  1108. /*  Check for line staring with:                                     */
  1109. /*                                                                   */
  1110. /*  -  a bullet ("*", "-", or "o")                                   */
  1111. /*  -  an alphabetic id in form "(x)" or "x)"                        */
  1112. /*  -  a numeric id in form "(n)", "(nn)", "n)", "nn)", "n.", "nn."  */
  1113. /*  -  a roman numeral in form "(r)", "r)",  "r." or "r"             */
  1114. /*                                                                   */
  1115. /*===================================================================*/
  1116.  
  1117. static enum boolean isitem(
  1118.   const char *lineptr,                 /* Ptr to line text           */
  1119.   const int indent)                    /* Count of leading blanks    */
  1120. {
  1121.   const char *p ;
  1122.   int l ;
  1123.  
  1124.   lineptr += indent ;
  1125.  
  1126.   if ( (p = strchr(lineptr,' ')) == NULL )
  1127.   {
  1128.     return FALSE ;
  1129.   }
  1130.  
  1131.   if ( (l = p - lineptr) == 1 )
  1132.   {
  1133.     if ( strchr("*-o",*lineptr) != NULL )
  1134.     {
  1135.       return TRUE ;
  1136.     }
  1137.   }
  1138.   else
  1139.   {
  1140.     p-- ;
  1141.  
  1142.     if ( *lineptr == '(' && *p == ')' )
  1143.     {
  1144.       lineptr++ ;
  1145.       l -= 2 ;
  1146.       if ( l == 1 && isalpha(*lineptr) )
  1147.       {
  1148.         return TRUE ;
  1149.       }
  1150.       return (isnumbered(lineptr,l) | isroman(lineptr,l)) ;
  1151.     }
  1152.  
  1153.     if ( *p == ')' )
  1154.     {
  1155.       l-- ;
  1156.       if ( l == 1 && isalpha(*lineptr) )
  1157.       {
  1158.         return TRUE ;
  1159.       }
  1160.       return (isnumbered(lineptr,l) | isroman(lineptr,l)) ;
  1161.     }
  1162.  
  1163.     if ( *p == '.' )
  1164.     {
  1165.       l-- ;
  1166.       return (isnumbered(lineptr,l) | isroman(lineptr,l)) ;
  1167.     }
  1168.   }
  1169.   
  1170.   return isroman(lineptr,l) ;
  1171. }
  1172.  
  1173. /*===================================================================*/
  1174. /*                                                                   */
  1175. /*  isnumbered  -  check for one or two digit number                 */
  1176. /*  ----------                                                       */
  1177. /*                                                                   */
  1178. /*  This function checks that the argument passed is either a one    */
  1179. /*  or two digit number.                                             */
  1180. /*                                                                   */
  1181. /*===================================================================*/
  1182.  
  1183. static enum boolean isnumbered(
  1184.   const char *p,                        /* Ptr to item number        */
  1185.   const int l)                          /* Length of item number     */
  1186. {
  1187.   if ( l == 1 )
  1188.   {
  1189.     if ( isdigit(*p) )
  1190.     {
  1191.       return TRUE ;
  1192.     }
  1193.   }
  1194.   else if ( l == 2 )
  1195.   {
  1196.     if ( isdigit(*p) && isdigit(*(p+1)) )
  1197.     {
  1198.       return TRUE ;
  1199.     }
  1200.   }
  1201.   return FALSE ;
  1202. }
  1203.  
  1204. /*===================================================================*/
  1205. /*                                                                   */
  1206. /*  isroman  -  check for roman numeral                              */
  1207. /*  -------                                                          */
  1208. /*                                                                   */
  1209. /*  This function checks that the argument passed is a roman numeral */
  1210. /*  in the range one to twenty (i - xx).                             */
  1211. /*                                                                   */
  1212. /*===================================================================*/
  1213.  
  1214. static enum boolean isroman(
  1215.   const char *p,                        /* Ptr to item number        */
  1216.   const int l)                          /* Length of item number     */
  1217. {
  1218.   static const char *const roman[] =
  1219.   {
  1220.     "i",
  1221.     "v",
  1222.     "x",
  1223.     "ii",
  1224.     "iv",
  1225.     "vi",
  1226.     "ix",
  1227.     "xi",
  1228.     "xv",
  1229.     "xx",
  1230.     "vii",
  1231.     "iii",
  1232.     "xii",
  1233.     "xiv",
  1234.     "xvi",
  1235.     "xix",
  1236.     "viii",
  1237.     "xiii",
  1238.     "xvii",
  1239.     "xviii"
  1240.   } ;
  1241.  
  1242.   static const char lroman[] =
  1243.   {
  1244.     1,         /* i            */
  1245.     1,         /* v            */
  1246.     1,         /* x            */
  1247.     2,         /* ii           */
  1248.     2,         /* iv           */
  1249.     2,         /* vi           */
  1250.     2,         /* ix           */
  1251.     2,         /* xi           */
  1252.     2,         /* xv           */
  1253.     2,         /* xx           */
  1254.     3,         /* vii          */
  1255.     3,         /* iii          */
  1256.     3,         /* xii          */
  1257.     3,         /* xiv          */
  1258.     3,         /* xvi          */
  1259.     3,         /* xix          */
  1260.     4,         /* viii         */
  1261.     4,         /* xiii         */
  1262.     4,         /* xvii         */
  1263.     5,         /* xviii        */
  1264.     99         /* end of list  */
  1265.   } ;
  1266.  
  1267.   int i,j ;
  1268.  
  1269.   if ( strspn(p,"ivx") != l || l > 5 )
  1270.   {
  1271.     return FALSE ;
  1272.   }
  1273.  
  1274.   for ( i = 0 ; l > lroman[i] ; i++ ) ;
  1275.   
  1276.   for ( ; l == lroman[i] ; i++ )
  1277.   {
  1278.     j = 0 ;
  1279.     while ( p[j] == roman[i][j] )
  1280.    {
  1281.      j++ ;
  1282.      if ( j == l )
  1283.      {
  1284.        return TRUE ;
  1285.      }
  1286.    }
  1287.   }
  1288.   return FALSE ;
  1289. }
  1290.  
  1291. /*===================================================================*/
  1292. /*                                                                   */
  1293. /*  isterm  -  test for definition list item                         */
  1294. /*  ------                                                           */
  1295. /*                                                                   */
  1296. /*  Assume a line starts with a definition term if:                  */
  1297. /*                                                                   */
  1298. /*  - line is indented less than following line                      */
  1299. /*  - line extends beyond ident of following line                    */
  1300. /*  - indent of following line matches start of a word on this line  */
  1301. /*                                                                   */
  1302. /*===================================================================*/
  1303.  
  1304. static enum boolean isterm(
  1305.   const char *lineptr,                  /* Ptr to line text          */
  1306.   int len,                              /* Length of line            */
  1307.   int indent,                           /* Indent of this line       */
  1308.   int nextindent)                       /* Indent of next line       */
  1309. {
  1310.   if ( indent < nextindent &&
  1311.       len > nextindent &&
  1312.       lineptr[nextindent] != ' ' &&
  1313.       lineptr[nextindent-1] == ' ' )
  1314.   {
  1315.     return TRUE ;
  1316.   }
  1317.   return FALSE ;
  1318. }
  1319.  
  1320. /*===================================================================*/
  1321. /*                                                                   */
  1322. /*  istag  -  test for list of non-break GML tags                    */
  1323. /*  -----                                                            */
  1324. /*                                                                   */
  1325. /*  This function tests the start of the line for the presence of    */
  1326. /*  GML tags which are not required to start on a separate line.     */
  1327. /*                                                                   */
  1328. /*===================================================================*/
  1329.  
  1330. static enum boolean istag(
  1331.   const char *lineptr)                  /* Ptr to line text          */
  1332. {
  1333.   static const char *const tags[] =
  1334.   {
  1335.     "HP",                           /* :hp and :ehp                  */
  1336.     "CIT",                          /* :cit and :ecit                */
  1337.     "Q",                            /* :q and :eq                    */
  1338.     "FNREF",                        /* :fnref and :efnref            */
  1339.     "HDREF"                         /* :hdref and :ehdref            */
  1340.   } ;
  1341.   #define ntags (sizeof(tags)/sizeof(char *))
  1342.  
  1343.   int i,j,c ;
  1344.   const char *tag ;
  1345.  
  1346.   lineptr++ ;
  1347.  
  1348.   if ( toupper(*lineptr) == 'E' )
  1349.   {
  1350.     lineptr++ ;
  1351.   }
  1352.  
  1353.   for ( i = 0 ; i < ntags ; i++ )
  1354.   {
  1355.     tag = tags[i] ;
  1356.     c = tag[0] ;
  1357.     j = 0 ;
  1358.     while ( c == toupper(lineptr[j]) )
  1359.     {
  1360.       j++ ;
  1361.       if ( (c = tag[j]) == '\0' )
  1362.       {
  1363.         return TRUE ;
  1364.       }
  1365.     }
  1366.   }
  1367.   return FALSE ;
  1368. }
  1369.  
  1370. /*===================================================================*/
  1371. /*                                                                   */
  1372. /*  isshort  -  check for short line                                 */
  1373. /*  -------                                                          */
  1374. /*                                                                   */
  1375. /*  This function checks for a short line ending with a punctuation  */
  1376. /*  character (i.e. a 'break' line).                                 */
  1377. /*                                                                   */
  1378. /*===================================================================*/
  1379.  
  1380. static enum boolean isshort(
  1381.   const char *lineptr,                  /* Ptr to line text          */
  1382.   const int len,                        /* Length of line            */
  1383.   const int brklen)                     /* Max length of short line  */
  1384. {
  1385.   if ( len > brklen || strchr(".:!?",lineptr[len-1]) == NULL )
  1386.   {
  1387.     return FALSE ;
  1388.   }
  1389.   return TRUE ;   
  1390. }
  1391.  
  1392. /*===================================================================*/
  1393. /*                                                                   */
  1394. /*  sethangingindent  -  scan forward setting hanging indent flag    */
  1395. /*  ----------------                                                 */
  1396. /*                                                                   */
  1397. /*  This function scans forward from the current line setting the    */
  1398. /*  hanging-indent flag on all lines which have the same or greater  */
  1399. /*  indent than the line following the current line. Null lines      */
  1400. /*  are ignored.                                                     */
  1401. /*                                                                   */
  1402. /*===================================================================*/
  1403.  
  1404. static void sethangingindent(
  1405.   int line,                             /* Current line number       */
  1406.   int nlines,                           /* Number of lines           */
  1407.   int nextindent,                       /* Indent of next line       */
  1408.   char *(*lineptrs)[],                  /* Ptr to line ptr array     */
  1409.   char (*lineflgs)[])                   /* Ptr to line flags array   */
  1410. {
  1411.   char *p ;
  1412.  
  1413.   for ( ++line ;
  1414.         line <= nlines &&
  1415.         (*(p = (*lineptrs)[line]) == '\0' ||
  1416.         (leadblanks(p) >= nextindent)) ;
  1417.         line++ )
  1418.   {
  1419.     if ( *p != '\0' )
  1420.     {
  1421.       (*lineflgs)[line] |= hinflg ;
  1422.     }
  1423.   }
  1424. }
  1425.  
  1426. /*===================================================================*/
  1427. /*                                                                   */
  1428. /*  putdup  -  write 'n' duplicate characters to output file         */
  1429. /*  ------                                                           */
  1430. /*                                                                   */
  1431. /*  This function writes a single character repeatedly to the        */
  1432. /*  1stWord+ document output file.                                   */
  1433. /*                                                                   */
  1434. /*===================================================================*/
  1435.  
  1436. static void putdup(
  1437.   const int c,                          /* Character to be repeated  */
  1438.   const int n,                          /* Duplication count         */
  1439.   FILE *fileptr)                        /* File pointer              */
  1440. {
  1441.   int i ;
  1442.  
  1443.   for ( i = 0 ; i < n ; i++ )
  1444.   {
  1445.     fputc(c,fileptr) ;
  1446.   }
  1447. }
  1448.  
  1449. /*===================================================================*/
  1450.